{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c3e2458f-d038-4845-93a0-d4ad830f9f90",
   "metadata": {},
   "source": [
    "# LangChain 核心模块学习：Chains\n",
    "\n",
    "对于简单的大模型应用，单独使用语言模型（LLMs）是可以的。\n",
    "\n",
    "**但更复杂的大模型应用需要将 `LLMs` 和 `Chat Models` 链接在一起 - 要么彼此链接，要么与其他组件链接。**\n",
    "\n",
    "LangChain 为这种“链式”应用程序提供了 `Chain` 接口。\n",
    "\n",
    "LangChain 以通用方式定义了 `Chain`，它是对组件进行调用序列的集合，其中可以包含其他链。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9cf0d43-107b-47ae-9e2c-2edaec38c800",
   "metadata": {},
   "source": [
    "## Chain Class 基类\n",
    "\n",
    "类继承关系：\n",
    "\n",
    "```\n",
    "Chain --> <name>Chain  # Examples: LLMChain, MapReduceChain, RouterChain\n",
    "```\n",
    "\n",
    "**代码实现：https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/chains/base.py**\n",
    "\n",
    "```python\n",
    "# 定义一个名为Chain的基础类\n",
    "class Chain(Serializable, Runnable[Dict[str, Any], Dict[str, Any]], ABC):\n",
    "    \"\"\"为创建结构化的组件调用序列的抽象基类。\n",
    "    \n",
    "    链应该用来编码对组件的一系列调用，如模型、文档检索器、其他链等，并为此序列提供一个简单的接口。\n",
    "    \n",
    "    Chain接口使创建应用程序变得容易，这些应用程序是：\n",
    "    - 有状态的：给任何Chain添加Memory可以使它具有状态，\n",
    "    - 可观察的：向Chain传递Callbacks来执行额外的功能，如记录，这在主要的组件调用序列之外，\n",
    "    - 可组合的：Chain API足够灵活，可以轻松地将Chains与其他组件结合起来，包括其他Chains。\n",
    "    \n",
    "    链公开的主要方法是：\n",
    "    - `__call__`：链是可以调用的。`__call__`方法是执行Chain的主要方式。它将输入作为一个字典接收，并返回一个字典输出。\n",
    "    - `run`：一个方便的方法，它以args/kwargs的形式接收输入，并将输出作为字符串或对象返回。这种方法只能用于一部分链，不能像`__call__`那样返回丰富的输出。\n",
    "    \"\"\"\n",
    "\n",
    "    # 调用链\n",
    "    def invoke(\n",
    "        self, input: Dict[str, Any], config: Optional[runnableConfig] = None\n",
    "    ) -> Dict[str, Any]:\n",
    "        \"\"\"传统调用方法。\"\"\"\n",
    "        return self(input, **(config or {}))\n",
    "\n",
    "    # 链的记忆，保存状态和变量\n",
    "    memory: Optional[BaseMemory] = None\n",
    "    \"\"\"可选的内存对象，默认为None。\n",
    "    内存是一个在每个链的开始和结束时被调用的类。在开始时，内存加载变量并在链中传递它们。在结束时，它保存任何返回的变量。\n",
    "    有许多不同类型的内存，请查看内存文档以获取完整的目录。\"\"\"\n",
    "\n",
    "    # 回调，可能用于链的某些操作或事件。\n",
    "    callbacks: Callbacks = Field(default=None, exclude=True)\n",
    "    \"\"\"可选的回调处理程序列表（或回调管理器）。默认为None。\n",
    "    在对链的调用的生命周期中，从on_chain_start开始，到on_chain_end或on_chain_error结束，都会调用回调处理程序。\n",
    "    每个自定义链可以选择调用额外的回调方法，详细信息请参见Callback文档。\"\"\"\n",
    "\n",
    "    # 是否详细输出模式\n",
    "    verbose: bool = Field(default_factory=_get_verbosity)\n",
    "    \"\"\"是否以详细模式运行。在详细模式下，一些中间日志将打印到控制台。默认值为`langchain.verbose`。\"\"\"\n",
    "\n",
    "    # 与链关联的标签\n",
    "    tags: Optional[List[str]] = None\n",
    "    \"\"\"与链关联的可选标签列表，默认为None。\n",
    "    这些标签将与对这个链的每次调用关联起来，并作为参数传递给在`callbacks`中定义的处理程序。\n",
    "    你可以使用这些来例如识别链的特定实例与其用例。\"\"\"\n",
    "\n",
    "    # 与链关联的元数据\n",
    "    metadata: Optional[Dict[str, Any]] = None\n",
    "    \"\"\"与链关联的可选元数据，默认为None。\n",
    "    这些元数据将与对这个链的每次调用关联起来，并作为参数传递给在`callbacks`中定义的处理程序。\n",
    "    你可以使用这些来例如识别链的特定实例与其用例。\"\"\"\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d51fbb4-1d8e-4ec1-8c55-ec70247d4d64",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "c81a7df0-26c7-4eb8-92f1-cc54445cf507",
   "metadata": {},
   "source": [
    "## LLMChain\n",
    "\n",
    "LLMChain 是 LangChain 中最简单的链，作为其他复杂 Chains 和 Agents 的内部调用，被广泛应用。\n",
    "\n",
    "一个LLMChain由PromptTemplate和语言模型（LLM or Chat Model）组成。它使用直接传入（或 memory 提供）的 key-value 来规范化生成 Prompt Template（提示模板），并将生成的 prompt （格式化后的字符串）传递给大模型，并返回大模型输出。\n",
    "\n",
    "![](../images/llm_chain.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "757a67a6-c1aa-4dde-94ef-fb9865dc634c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import OpenAI\n",
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "llm = OpenAI(model_name=\"gpt-3.5-turbo-instruct\", temperature=0.9, max_tokens=500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "0b863511-ee01-43e8-8540-4e3f109a5a1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"product\"],\n",
    "    template=\"给制造{product}的有限公司取10个好名字，并给出完整的公司名称\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b877560c-cb66-41ad-b484-b2df2a60a00d",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\Dev\\anaconda3\\envs\\langchain\\lib\\site-packages\\langchain_core\\_api\\deprecation.py:119: LangChainDeprecationWarning: The class `LLMChain` was deprecated in LangChain 0.1.17 and will be removed in 0.3.0. Use RunnableSequence, e.g., `prompt | llm` instead.\n",
      "  warn_deprecated(\n",
      "D:\\Dev\\anaconda3\\envs\\langchain\\lib\\site-packages\\langchain_core\\_api\\deprecation.py:119: LangChainDeprecationWarning: The method `Chain.run` was deprecated in langchain 0.1.0 and will be removed in 0.3.0. Use invoke instead.\n",
      "  warn_deprecated(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "1. 极致科技图形有限公司（UltimateTech Graphics Co., Ltd.）\n",
      "2. 希望视界科技股份有限公司（VisionTech Inc.）\n",
      "3. 创新绘图解决方案有限公司（Innovative Graphics Solutions Co., Ltd.）\n",
      "4. 光谱渲染器科技有限公司（Spectrum Renderer Technology Co., Ltd.）\n",
      "5. 卓越图像处理器有限公司（Excellence Image Processor Co., Ltd.）\n",
      "6. 未来图像生成科技有限公司（Future Image Generation Technology Co., Ltd.）\n",
      "7. 超强渲染引擎有限公司（Superior Rendering Engine Co., Ltd.）\n",
      "8. 精密视觉计算有限公司（Precision Visual Computing Co., Ltd.）\n",
      "9. 领先图像加速器有限公司（Leading Image Accelerator Co., Ltd.）\n",
      "10. 高性能图形处理有限公司（High Performance Graphics Processing Co., Ltd.）\n"
     ]
    }
   ],
   "source": [
    "from langchain.chains import LLMChain\n",
    "\n",
    "chain = LLMChain(llm=llm, prompt=prompt)\n",
    "print(chain.run({\n",
    "    'product': \"性能卓越的GPU\"\n",
    "    }))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "727ccd76-0c6a-425b-bfc7-23d368c296f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain.verbose = True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "1766e01a-c5c4-4a74-9ebb-ecfc84101ba2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.verbose"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cfa71d7c-2859-47e1-9815-4be2ec9dbd74",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new LLMChain chain...\u001B[0m\n",
      "Prompt after formatting:\n",
      "\u001B[32;1m\u001B[1;3m给制造性能卓越的GPU的有限公司取10个好名字，并给出完整的公司名称\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "\n",
      "\n",
      "1. 新视界科技(Innovision Technology)\n",
      "2. 巅峰显卡(Overdrive Graphics)\n",
      "3. 强劲视界(PowerView Graphics)\n",
      "4. 飞驰科技(RapidTech)\n",
      "5. 腾飞显卡(Soar Graphics)\n",
      "6. 全力图形(PrimeGraphix)\n",
      "7. 璀璨科技(BrightStar Technology)\n",
      "8. 创新绘图(Innovate Graphics)\n",
      "9. 高速视界(SpeedView Graphics)\n",
      "10. 卓越图形(Pinnacle Graphics)\n",
      "\n",
      "完整公司名称:\n",
      "1. 新视界科技有限公司(Innovision Technology Co., Ltd.)\n",
      "2. 巅峰显卡有限公司(Overdrive Graphics Co., Ltd.)\n",
      "3. 强劲视界有限公司(PowerView Graphics Co., Ltd.)\n",
      "4. 飞驰科技有限公司(RapidTech Co., Ltd.)\n",
      "5. 腾飞显卡有限公司(Soar Graphics Co., Ltd.)\n",
      "6. 全力图形有限公司(PrimeGraphix Co., Ltd.)\n",
      "7. 璀璨科技有限公司(BrightStar Technology Co., Ltd.)\n",
      "8. 创新绘图有限公司(Innovate Graphics Co., Ltd.)\n",
      "9. 高速视界有限公司(SpeedView Graphics Co., Ltd.)\n",
      "10. 卓越图形有限公司(Pinnacle Graphics Co., Ltd.)\n"
     ]
    }
   ],
   "source": [
    "print(chain.run({\n",
    "    'product': \"性能卓越的GPU\"\n",
    "    }))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ac5411e7-b8ec-4c31-b659-deb44af038df",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "99cbf75e-98f4-4c99-b8a7-9a48cc28c7bc",
   "metadata": {},
   "source": [
    "## Sequential Chain\n",
    "\n",
    "串联式调用语言模型（将一个调用的输出作为另一个调用的输入）。\n",
    "\n",
    "顺序链（Sequential Chain ）允许用户连接多个链并将它们组合成执行特定场景的流水线（Pipeline）。有两种类型的顺序链：\n",
    "\n",
    "- SimpleSequentialChain：最简单形式的顺序链，每个步骤都具有单一输入/输出，并且一个步骤的输出是下一个步骤的输入。\n",
    "- SequentialChain：更通用形式的顺序链，允许多个输入/输出。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e192c8c-49fc-4d04-8444-e6aa6bd7b725",
   "metadata": {},
   "source": [
    "### 使用 SimpleSequentialChain 实现戏剧摘要和评论（单输入/单输出）\n",
    "\n",
    "![](../images/simple_sequential_chain_0.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "c4dfa461-90a5-458d-b976-497b51da53f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from typing import Optional\n",
    "# 在输出前添加前缀\n",
    "class PrefixOutputParser(StrOutputParser):\n",
    "    prefix: Optional[str] = None\n",
    "    def parse(self, text):\n",
    "        return f\"{self.prefix}{text}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "a4d192a2-d563-4ab7-979f-640fa34f1914",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个 LLMChain，用于根据剧目的标题撰写简介。\n",
    "\n",
    "llm = OpenAI(temperature=0.7, max_tokens=1000)\n",
    "\n",
    "template = \"\"\"你是一位剧作家。根据戏剧的标题，你的任务是为该标题写一个简介。\n",
    "\n",
    "标题：{title}\n",
    "剧作家：以下是对上述戏剧的简介：\"\"\"\n",
    "prefix_parser = PrefixOutputParser(prefix=\"Synopsis: \")\n",
    "prompt_template = PromptTemplate(input_variables=[\"title\"], template=template)\n",
    "synopsis_chain = LLMChain(llm=llm, prompt=prompt_template,output_parser=prefix_parser)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "3f7d429b-7ba7-4643-bd9f-fdb737ebf964",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个LLMChain，用于根据剧情简介撰写一篇戏剧评论。\n",
    "# llm = OpenAI(temperature=0.7, max_tokens=1000)\n",
    "template = \"\"\"你是《纽约时报》的戏剧评论家。根据剧情简介，你的工作是为该剧撰写一篇评论。\n",
    "\n",
    "剧情简介：\n",
    "{synopsis}\n",
    "\n",
    "以下是来自《纽约时报》戏剧评论家对上述剧目的评论：\"\"\"\n",
    "prefix_parser = PrefixOutputParser(prefix=\"Review: \")\n",
    "prompt_template = PromptTemplate(input_variables=[\"synopsis\"], template=template)\n",
    "review_chain = LLMChain(llm=llm, prompt=prompt_template,output_parser=prefix_parser)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5265129-5ccd-4e29-b221-0ec24eb84c2b",
   "metadata": {},
   "source": [
    "![](../images/simple_sequential_chain_1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "de4d816e-16e1-4382-9064-6c03e5841ea2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个SimpleSequentialChain，按顺序运行这两个链\n",
    "from langchain.chains import SimpleSequentialChain\n",
    "\n",
    "overall_chain = SimpleSequentialChain(chains=[synopsis_chain, review_chain], verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "d503ac4f-e337-4436-86a1-7fd937efb06a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new SimpleSequentialChain chain...\u001B[0m\n",
      "\u001B[36;1m\u001B[1;3mSynopsis: \n",
      "《三体人不是无法战胜的》讲述了一个关于人类与外星文明的对抗故事。在地球上，一群科学家发现了来自三体星系的信号，他们决定与三体人建立联系并探索宇宙的奥秘。然而，随着更多的接触，他们发现三体人拥有强大的科技和战斗能力，对地球构成了巨大的威胁。在一场殊死的抗争中，人类必须团结一致，发挥出自己的智慧和勇气，才有可能战胜来自宇宙的敌人。在战斗的过程中，人类也深刻反思自己的存在意义和生命的价值。最终，人类能否成功击败三体人，成为宇宙中的强者？或者，他们将被三体人彻底消灭？这场关于文明与生存的较量，将在舞台上激发出无穷的冲突和激情，让观众领略到人类的无畏精神和对未知世界的无限探索欲望。\u001B[0m\n",
      "\u001B[33;1m\u001B[1;3mReview: \n",
      "\n",
      "《三体人不是无法战胜的》是一部令人兴奋和挑战性的戏剧作品。它讲述了一个关于文明与生存的较量，让观众在剧场中感受到宇宙的无限广阔和人类的勇气与智慧。\n",
      "\n",
      "故事的主线围绕着人类与来自三体星系的外星文明三体人的对抗展开。随着人类与三体人的接触越来越深入，观众也逐渐意识到这个敌人拥有强大的科技和战斗能力，对地球的威胁不容小觑。在这场殊死的抗争中，人类必须团结一致，发挥出自己的智慧和勇气，才有可能战胜来自宇宙的敌人。这种紧张的氛围让观众也深深地陷入到剧情之中，体会到战斗的艰辛和希望的光芒。\n",
      "\n",
      "除了戏剧性的对抗，剧中还融入了对人类存在意义和生命价值的深刻反思。在面对来自宇宙的强敌时，人类不仅要保卫自己，更要思考自己的存在意义。这种对人类内心世界的探索，让观众感受到更深层次的情感共鸣。\n",
      "\n",
      "此外，剧中的舞台布景和特效也十分精彩，为故事增添了更多的视觉冲击力。演员们的精彩表演更是将剧情推向高潮，让观众全身心地投入到故事之中。\n",
      "\n",
      "总的来说，《三体人不是无法战胜的》是一部挑战人类想象力和思考的戏剧作品，它让观众在戏剧舞台上感受到宇宙的无限广阔和人类的无畏精神。无论是科幻迷还是戏剧爱好者，都不应错过这部令人兴奋的作品。\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n"
     ]
    }
   ],
   "source": [
    "review = overall_chain.run(\"三体人不是无法战胜的\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "ce4d75e1-8c57-4583-be7d-60a3488e35b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new SimpleSequentialChain chain...\u001B[0m\n",
      "\u001B[36;1m\u001B[1;3mSynopsis: \n",
      "\n",
      "《星球大战第九季》是一部充满科幻冒险和史诗般战争的剧作。故事发生在遥远的星系，继承了前八部电影的传统，讲述了反抗军与邪恶的第一秩序之间的持续战争。一场新的决战即将来临，反抗军的领袖们将不惜一切代价与第一秩序的黑暗势力抗衡。同时，一位神秘的新角色出现，他的出现将改变整个星球的命运。在这场冒险中，古老的力量将被唤醒，英雄们将面临艰难的选择和挑战，他们的命运和整个星球的未来都将受到考验。《星球大战第九季》将带领观众进入一个令人惊叹的宇宙，让他们经历惊心动魄的战斗和感人的情感故事，同时也探索着关于力量、勇气和宿命的深刻思考。这部戏剧将为观众带来一场视听盛宴，让他们沉浸在星球大战的世界中，直到最后一刻。\u001B[0m\n",
      "\u001B[33;1m\u001B[1;3mReview: \n",
      "\n",
      "《星球大战第九季》是一部令人兴奋的戏剧作品，它将观众带入一个充满冒险和战争的遥远星系。这部剧作不仅延续了前八部电影的传统，同时也为观众带来了全新的故事线和角色。\n",
      "\n",
      "在这部戏剧中，反抗军与第一秩序之间的战争达到了新的高潮，英雄们将面临艰难的选择和挑战。同时，一位神秘的新角色的出现也为故事增添了更多的悬念和惊喜。观众们将跟随这些英雄一起经历惊心动魄的战斗，同时也会沉浸在关于力量、勇气和宿命的深刻思考中。\n",
      "\n",
      "《星球大战第九季》不仅仅是一部科幻冒险作品，它也是一部探讨人性、宿命和勇气的戏剧。剧中的角色们都面临着自己的内心挣扎和成长，这让他们更加真实和感人。此外，精彩的特效和动作场面也让观众们仿佛置身于星球大战的世界中，一起感受着其中的紧张和刺激。\n",
      "\n",
      "总的来说，《星球大战第九季》是一部值得观看的戏剧作品，它将为观众带来一场视听盛宴，同时也会让他们思考更深层次的问题。无论是星球大战的粉丝还是普通观众，都会被这部戏剧所吸引，直到最后一刻。我强烈推荐观众们去欣赏这部令人惊叹的作品，相信它会给大家带来一次难忘的冒险之旅。\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n"
     ]
    }
   ],
   "source": [
    "review = overall_chain.run(\"星球大战第九季\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fe32f1d-475d-4211-9b32-0c66dd8bff01",
   "metadata": {},
   "source": [
    "### 使用 SequentialChain 实现戏剧摘要和评论（多输入/多输出）\n",
    "\n",
    "![](../images/sequential_chain_0.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "2a04d84f-15c6-4a8d-a4db-200dfa405afa",
   "metadata": {},
   "outputs": [],
   "source": [
    "# # 这是一个 LLMChain，根据剧名和设定的时代来撰写剧情简介。\n",
    "llm = OpenAI(temperature=.7, max_tokens=1000)\n",
    "template = \"\"\"你是一位剧作家。根据戏剧的标题和设定的时代，你的任务是为该标题写一个简介。\n",
    "\n",
    "标题：{title}\n",
    "时代：{era}\n",
    "剧作家：以下是对上述戏剧的简介：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"title\", \"era\"], template=template)\n",
    "# output_key\n",
    "synopsis_chain = LLMChain(llm=llm, prompt=prompt_template, output_key=\"synopsis\", verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "250afe66-e014-4097-9798-f9ba812023fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个LLMChain，用于根据剧情简介撰写一篇戏剧评论。\n",
    "\n",
    "template = \"\"\"你是《纽约时报》的戏剧评论家。根据该剧的剧情简介，你需要撰写一篇关于该剧的评论。\n",
    "\n",
    "剧情简介：\n",
    "{synopsis}\n",
    "\n",
    "来自《纽约时报》戏剧评论家对上述剧目的评价：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"synopsis\"], template=template)\n",
    "review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key=\"review\", verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "9eb46f6d-841b-4b87-9ed5-a5913ef9aec5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains import SequentialChain\n",
    "\n",
    "m_overall_chain = SequentialChain(\n",
    "    chains=[synopsis_chain, review_chain],\n",
    "    input_variables=[\"era\", \"title\"],\n",
    "    # Here we return multiple variables\n",
    "    output_variables=[\"synopsis\", \"review\"],\n",
    "    verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "5a4a12ef-da2a-42ad-8044-fb71aedd3e2d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new SequentialChain chain...\u001B[0m\n",
      "\n",
      "\n",
      "\u001B[1m> Entering new LLMChain chain...\u001B[0m\n",
      "Prompt after formatting:\n",
      "\u001B[32;1m\u001B[1;3m你是一位剧作家。根据戏剧的标题和设定的时代，你的任务是为该标题写一个简介。\n",
      "\n",
      "标题：三体人不是无法战胜的\n",
      "时代：二十一世纪的新中国\n",
      "剧作家：以下是对上述戏剧的简介：\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "\n",
      "\n",
      "\u001B[1m> Entering new LLMChain chain...\u001B[0m\n",
      "Prompt after formatting:\n",
      "\u001B[32;1m\u001B[1;3m你是《纽约时报》的戏剧评论家。根据该剧的剧情简介，你需要撰写一篇关于该剧的评论。\n",
      "\n",
      "剧情简介：\n",
      "\n",
      "\n",
      "在二十一世纪的新中国，随着科技的飞速发展，人类的生活发生了巨大的变化。然而，这个新时代也带来了前所未有的挑战。在这个充满未知的世界中，一群勇敢的科学家们发现了一个令人震惊的事实：宇宙中存在着一种强大的外星生物——三体人。\n",
      "\n",
      "这些来自遥远星球的三体人具有超越人类想象的科技能力，他们对地球的掌控力越来越强，人类面临着前所未有的危机。然而，在面对这个强大的敌人时，人类并没有束手就擒，他们开始团结一心，积极寻求对抗三体人的方法。\n",
      "\n",
      "在这场充满惊险与挑战的斗争中，人类发现了自己内心深处的潜力和勇气。他们不再害怕三体人，而是勇敢地面对他们，最终找到了战胜三体人的方法。\n",
      "\n",
      "这部戏剧将展现人类面对未知的勇气和决心，以及科技发展带来的利弊。它将让观众感受到人类的生命力和无穷的希望，同时也提醒我们要谨慎对待科技发展带来的挑战。三体人并不是无法战胜的，只要我们团结一心，勇敢面对，就能够战胜一切困难。\n",
      "\n",
      "来自《纽约时报》戏剧评论家对上述剧目的评价：\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'title': '三体人不是无法战胜的',\n",
       " 'era': '二十一世纪的新中国',\n",
       " 'synopsis': '\\n\\n在二十一世纪的新中国，随着科技的飞速发展，人类的生活发生了巨大的变化。然而，这个新时代也带来了前所未有的挑战。在这个充满未知的世界中，一群勇敢的科学家们发现了一个令人震惊的事实：宇宙中存在着一种强大的外星生物——三体人。\\n\\n这些来自遥远星球的三体人具有超越人类想象的科技能力，他们对地球的掌控力越来越强，人类面临着前所未有的危机。然而，在面对这个强大的敌人时，人类并没有束手就擒，他们开始团结一心，积极寻求对抗三体人的方法。\\n\\n在这场充满惊险与挑战的斗争中，人类发现了自己内心深处的潜力和勇气。他们不再害怕三体人，而是勇敢地面对他们，最终找到了战胜三体人的方法。\\n\\n这部戏剧将展现人类面对未知的勇气和决心，以及科技发展带来的利弊。它将让观众感受到人类的生命力和无穷的希望，同时也提醒我们要谨慎对待科技发展带来的挑战。三体人并不是无法战胜的，只要我们团结一心，勇敢面对，就能够战胜一切困难。',\n",
       " 'review': '\\n\\n《三体人》是一部令人惊叹的戏剧作品，它将观众带入了一个充满未知和危险的世界，同时又展现了人类的勇气和决心。\\n\\n该剧以新时代中国为背景，通过科技发展带来的巨大变化，揭示了人类在面对前所未有的挑战时所展现出的无畏和团结力量。故事中的三体人作为强大的外星生物，给人类带来了巨大的威胁，但人类并没有畏缩，而是积极寻求对抗的方法，并最终战胜了这一强大的敌人。\\n\\n该剧不仅仅是一部科幻作品，更是一部关于人类内心潜能和生命力的探讨。它让观众感受到人类的不屈精神和无穷的希望，同时也提醒我们要谨慎对待科技发展带来的挑战。与其说三体人是无法战胜的敌人，不如说它们是一个激励人类团结一心、勇敢面对挑战的催化剂。\\n\\n该剧的精彩表演和紧凑的剧情让观众全程沉浸其中，同时也让人们反思人类面对未知的态度。它将给观众带来深刻的思考和感动，是一部不可错过的戏剧作品。'}"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_overall_chain({\"title\":\"三体人不是无法战胜的\", \"era\": \"二十一世纪的新中国\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c20cf4e-25b4-453d-9f7a-84138ca25cf8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5ef1db6e-3da4-4f9b-9707-0f30aa293dd7",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "8b6836f0-213d-4cac-abc9-3617831be3db",
   "metadata": {},
   "source": [
    "### Homework\n",
    "\n",
    "#### (已完成)使用 OutputParser 优化 overall_chain 输出格式，区分 synopsis_chain 和 review_chain 的结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c7edb0a-675d-40c0-9f5d-d58f0170ce72",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
